package chapter01.source05;

import java.awt.*;
import java.awt.event.*;
import java.awt.print.PrinterException;
import java.io.*;
import java.text.*;
import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import javax.swing.ListSelectionModel;
import javax.swing.event.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.undo.*;

public class Notepad {

    JFrame frame;
    String Title = "Untitled";
    JMenuBar menubar ;
    JTextArea textarea;

    boolean edited = false;
    File file = null;
    JPanel status;

    UndoManager undomanager;

    public static void main(String[] args) {

        new Notepad();

    }

    Notepad(){
        frame = new JFrame(Title);
        textarea = new JTextArea();

        frame.setSize(1000, 750);
        frame.setLocationRelativeTo(null);
        frame.setJMenuBar(menubar = addmenu());
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        textarea.setLineWrap(true);
        textarea.setWrapStyleWord(true);


        textarea.getDocument().addDocumentListener(new DocumentListener(){
            public void insertUpdate(DocumentEvent e) {
                edited = true;
                frame.getJMenuBar().getMenu(1).getItem(0).setEnabled(undomanager.canUndo());
                frame.getJMenuBar().getMenu(1).getItem(1).setEnabled(undomanager.canRedo());
            }
            public void removeUpdate(DocumentEvent e) {
                edited = true;
                frame.getJMenuBar().getMenu(1).getItem(0).setEnabled(undomanager.canUndo());
                frame.getJMenuBar().getMenu(1).getItem(1).setEnabled(undomanager.canRedo());
            }
            public void changedUpdate(DocumentEvent e) {
                edited = true;
                frame.getJMenuBar().getMenu(1).getItem(0).setEnabled(undomanager.canUndo());
                frame.getJMenuBar().getMenu(1).getItem(1).setEnabled(undomanager.canRedo());
            }
        });
        frame.add(BorderLayout.CENTER,textarea);
        frame.add(new JScrollPane(textarea , JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),BorderLayout.CENTER);
        frame.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e) {
                frame.getJMenuBar().getMenu(0).getItem(7).doClick();
            }});


        status = new JPanel(new BorderLayout());

        JLabel label= new JLabel("Ln 1, Col 1");
        label.setFont(new Font("Arial",Font.PLAIN,15));
        label.setBorder(BorderFactory.createEmptyBorder(0, 100, 0, 100));
        status.add(label,BorderLayout.EAST);

        textarea.addCaretListener(new CaretListener(){
            public void caretUpdate(CaretEvent e) {
                int abc =  textarea.getCaretPosition();
                int x = 0;
                try {
                    x = textarea.getLineOfOffset(abc);
                    abc = abc - textarea.getLineStartOffset(x);
                } catch (BadLocationException e1) {}
                label.setText("Ln " + (x+1)  +", Col " + (abc+1));
            }
        });
        textarea.getDocument().addUndoableEditListener(undomanager = new UndoManager());

        frame.setVisible(true);
    }

    public JMenuBar addmenu(){
        JMenuBar menubar = new JMenuBar();
        menubar.setFocusTraversalKeysEnabled(true);
        JMenu[] menu = new JMenu[4];

        int key = KeyEvent.META_DOWN_MASK;

        menu[0] = createmenu("File",new String[]{"Save As" , "Print"} ,addmenuitem("New" , "new",KeyEvent.VK_N,key),addmenuitem("Open" , "open",KeyEvent.VK_O,key),addmenuitem("Save" , "save",KeyEvent.VK_S,key),addmenuitem("Save As" , "save as",0,0),addmenuitem("Print" , "print",KeyEvent.VK_P,key),addmenuitem("Exit" , "exit",KeyEvent.VK_Q,key));

        menu[1] = createmenu("Edit",new String[]{"Redo","Delete","Go To"},addmenuitem("Undo" , "undo",KeyEvent.VK_Z,key),addmenuitem("Redo" , "redo",KeyEvent.VK_Z,KeyEvent.SHIFT_DOWN_MASK+key),addmenuitem("Cut" , "cut",KeyEvent.VK_X,key),addmenuitem("Copy" , "copy",KeyEvent.VK_C,key),addmenuitem("Paste" , "paste",KeyEvent.VK_V,key),addmenuitem("Delete" , "delete",KeyEvent.VK_BACK_SPACE,KeyEvent.SHIFT_DOWN_MASK),addmenuitem("Find/Replace" , "find",KeyEvent.VK_F,key),addmenuitem("Go To" , "go to",KeyEvent.VK_G,key), addmenuitem("Select All" , "select all",KeyEvent.VK_A,key),addmenuitem("Time/Date" , "time/date",KeyEvent.VK_F5,0));
        menu[1].getItem(0).setEnabled(false);
        menu[1].getItem(1).setEnabled(false);

        menu[2] = createmenu("Format" , new String[]{}, addmenuitem("Format","format",0,0));

        JCheckBoxMenuItem wordwrap = (new JCheckBoxMenuItem("Word Wrap"));
        wordwrap.setActionCommand("wordwrap");
        wordwrap.setSelected(true);
        wordwrap.addActionListener(new itemlistener());
        menu[2].add(wordwrap);

        menu[3] = new JMenu("View");
        JCheckBoxMenuItem  statusbar = new JCheckBoxMenuItem("Status Bar");
        statusbar.setEnabled(false);
        statusbar.setActionCommand("statusbar");
        statusbar.addActionListener(new itemlistener());
        menu[3].add(statusbar);

        for(JMenu m : menu){
            menubar.add(m);
            for(int i=0 ; i<m.getItemCount() ; i++)
                if(m.getItem(i)!=null)
                    m.getItem(i).addActionListener(new itemlistener());
        }

        return menubar;
    }

    public JMenu createmenu(String name,String[] sep , JMenuItem... items){
        JMenu menu = new JMenu(name);
        for(JMenuItem m : items){
            menu.add(m);
            for(String s: sep)if(m.getText().equals(s))menu.addSeparator();
        }

        return menu;
    }

    public JMenuItem addmenuitem(String name , String action , int s , int mo){
        JMenuItem abc = new JMenuItem(name);
        abc.setActionCommand(action);
        if(s!=0)
            abc.setAccelerator(KeyStroke.getKeyStroke(s, mo));
        return abc;
    }

    public Notepad newfile(){
        return new Notepad();
    }
    public File openfile(){
        JFileChooser jfc = new JFileChooser();
        jfc.showOpenDialog(frame);
        File file = jfc.getSelectedFile() ;
        if(file!=null){
            if(Title.equals("Untitled")&&!edited){
                frame.dispose();
            }
            Notepad note1 = new Notepad();
            FileReader fis;
            try {
                fis = new FileReader(file);
                int c = 0;
                while((c=fis.read())!=-1){
                    note1.textarea.append((char)c+"");
                }
                note1.Title = file.getName();
                note1.frame.setTitle(note1.Title);
                note1.frame.toFront();
                note1.edited=false;
                note1.file = file;
            } catch (Exception e) {}
        }

        return file;

    }
    public JDialog find(){

        JDialog findframe = new JDialog(frame , "find" , true);
        findframe.setLocation((int)frame.getLocationOnScreen().getX()+200 , (int)frame.getLocationOnScreen().getY()+200);
        findframe.setSize(400, 200);
        findframe.setLayout(null);


        JTextField findfield = new JTextField();
        JLabel findlabel = new JLabel("Find What:");
        findlabel.setBounds(10, 10, 100, 20);
        findfield.setBounds(findlabel.getX()+100, findlabel.getY(), 150, findlabel.getHeight());
        findframe.add(findlabel);
        findframe.add(findfield);

        JTextField replacefield = new JTextField();
        JLabel replacelabel = new JLabel("Replace With:");
        replacelabel.setBounds(10, 60, 100, 20);
        replacefield.setBounds(replacelabel.getX()+100, replacelabel.getY(), 150, replacelabel.getHeight());
        findframe.add(replacelabel);
        findframe.add(replacefield);

        JCheckBox matchcase = new JCheckBox("Match Case");
        matchcase.setBounds(270, 30, 150, 30);
        findframe.add(matchcase);

        JLabel status = new JLabel();
        status.setBounds(10, 130, 200, 20);
        findframe.add(status);

        JButton findnext = new JButton("Find Next");
        findnext .setBounds(10,100,120,20);
        findnext.addActionListener(new ActionListener(){
            int i=0;
            public void actionPerformed(ActionEvent e) {
                String text = textarea.getText();
                Pattern pat = Pattern.compile(findfield.getText());
                Matcher matcher=pat.matcher(text) ;
                if(matcher.find(i)){
                    textarea.setSelectionStart(matcher.start());
                    textarea.setSelectionEnd(i=matcher.end());
                }
                else{
                    status.setText("No more words found");
                    status.repaint();
                }
            }
        });
        findframe.add(findnext);

        JButton replace = new JButton("Replace");
        replace .setBounds(findnext.getX()+findnext.getWidth()+10,findnext.getY(),findnext.getWidth(),findnext.getHeight());
        replace.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                String text = textarea.getText();
                Pattern pattern = Pattern.compile(findfield.getText());

                Matcher match = pattern.matcher(text);
                if(match.find()){
                    System.out.println(match.start());
                    textarea.setText(match.replaceFirst(replacefield.getText()));
                }
                else{
                    status.setText("No more words found");
                    status.repaint();
                }

            }

        });
        findframe.add(replace);

        JButton replaceall = new JButton("Replace All");
        replaceall .setBounds(replace.getX()+replace.getWidth()+10,findnext.getY(),findnext.getWidth(),findnext.getHeight());
        replaceall.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                String text = textarea.getText();
                Pattern pattern;
                if(matchcase.isSelected()){
                    pattern = Pattern.compile(findfield.getText());
                }
                else{
                    pattern = Pattern.compile(findfield.getText(), Pattern.CASE_INSENSITIVE);
                }
                Matcher match = pattern.matcher(text);
                int i=0;
                if(match.find()){
                    int x=0;
                    while(match.find(x)){x=match.end();i++;}
                    textarea.setText(match.replaceAll(replacefield.getText()));
                }
                if(i==0)status.setText("No Words Found");
                else status.setText(i+" Word replaced");

                status.repaint();
            }

        });
        findframe.add(replaceall);

        findframe.setVisible(true);
        findframe.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        return findframe;

    }
    public void savefile(String text){
        if(Title.equals("Untitled")){
            JFileChooser jfc = new JFileChooser();
            jfc.showSaveDialog(frame);
            file = jfc.getSelectedFile();
            try {
                file.createNewFile();
                Title = file.getName();
            } catch (IOException e) {}
        }
        try (FileWriter fw=new FileWriter(file)){
            fw.write(text);
        } catch (IOException e) {}
        frame.setTitle(Title);

        edited = false;
    }

    public void format(){
        JDialog formatter = new JDialog(frame,"Format",true);
        formatter.setLocation(frame.getX()+200, frame.getY()+150);
        formatter.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

        JLabel sample = new JLabel("AaBbYyZz");
        Font prev = textarea.getFont();

        JPanel preview = new JPanel();
        sample.setPreferredSize(new Dimension(400,100));
        sample.setHorizontalAlignment(JLabel.CENTER);
        sample.setBorder(BorderFactory.createLineBorder(Color.BLACK , 2));
        sample.setFont(textarea.getFont());
        preview.add(sample);

        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints cons = new GridBagConstraints();
        JPanel north = new JPanel(layout);

        cons.insets = new Insets(0,50,10,50);

        JLabel font = new JLabel("Font :");
        font.setHorizontalAlignment(JLabel.LEFT);
        layout.setConstraints(font, cons);
        north.add(font);

        JLabel fontstyle = new JLabel("Font Style:");
        fontstyle.setHorizontalAlignment(JLabel.LEFT);
        layout.setConstraints(fontstyle, cons);
        north.add(fontstyle);

        cons.gridwidth = GridBagConstraints.REMAINDER;
        JLabel fontsize = new JLabel("Font Size:");
        fontsize.setHorizontalAlignment(JLabel.LEFT);
        layout.setConstraints(fontsize, cons);
        north.add(fontsize);

        JList<String> facelist = new JList<>(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
        facelist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        facelist.setVisibleRowCount(10);

        facelist.setSelectedValue(sample.getFont().getName(), true);
        facelist.addListSelectionListener(new ListSelectionListener(){
            public void valueChanged(ListSelectionEvent e) {
                Font f = sample.getFont();
                sample.setFont(new Font(facelist.getSelectedValue(),f.getStyle() , f.getSize()));
            }});
        north.add(facelist , cons);
        north.add(new JScrollPane(facelist));

        JList<String> stylelist = new JList<>(new String[]{"Regular" , "Bold" , "Italic" , "Bold Italic"});
        stylelist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        stylelist.setVisibleRowCount(10);
        stylelist.setFixedCellWidth(100);
        stylelist.setSelectedValue(sample.getFont().getStyle(), true);
        stylelist.addListSelectionListener(new ListSelectionListener(){
            public void valueChanged(ListSelectionEvent e) {
                Font f = sample.getFont();
                sample.setFont(new Font(f.getName() ,stylelist.getSelectedIndex(), f.getSize()));
            }
        });
        north.add(stylelist);
        north.add(new JScrollPane(stylelist));

        JList<Integer> sizelist = new JList<>(new Integer[]{5,6,7,8,9,10,11,12,14,16,18,20,22,24,26,28,32,36,48,72});
        sizelist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        sizelist.setFixedCellWidth(70);
        sizelist.setVisibleRowCount(10);
        sizelist.setSelectedValue(sample.getFont().getSize(), true);
        sizelist.addListSelectionListener(new ListSelectionListener(){
            public void valueChanged(ListSelectionEvent e) {
                Font f = sample.getFont();
                sample.setFont(new Font(f.getName(),f.getStyle() , sizelist.getSelectedValue()));
            }
        });
        north.add(sizelist);
        north.add(new JScrollPane(sizelist));

        formatter.add(BorderLayout.CENTER, north);

        JPanel buttons = new JPanel(new BorderLayout());
        JButton ok = new JButton("OK");
        ok.setPreferredSize(new Dimension(100,20));
        ok.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                textarea.setFont(sample.getFont());
                formatter.dispose();
                undomanager.addEdit(new UndoableEdit(){
                    public  void undo() throws CannotUndoException {textarea.setFont(prev);}
                    public boolean canUndo() {return true;}
                    public void redo() throws CannotRedoException {textarea.setFont(sample.getFont());}
                    public boolean canRedo() {return true;}
                    public void die() {}
                    public boolean addEdit(UndoableEdit anEdit) {return false;}
                    public boolean replaceEdit(UndoableEdit anEdit) {return false;}
                    public boolean isSignificant() {return true;}
                    public String getPresentationName() {return null;}
                    public String getUndoPresentationName() {return null;}
                    public String getRedoPresentationName() {return null;}
                });
            }});
        buttons.add(BorderLayout.NORTH , ok);

        JButton cancel = new JButton("CANCEL");
        cancel.setPreferredSize(new Dimension(100,20));
        cancel.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                formatter.dispose();
            }
        });
        buttons.add(BorderLayout.SOUTH,cancel);
        buttons.add(BorderLayout.CENTER , new JPanel());
        preview.add(buttons);

        formatter.add(BorderLayout.SOUTH, preview);

        formatter.pack();
        formatter.setVisible(true);
    }

    String edittext = "";
    class itemlistener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            if(e.getActionCommand().equals("new"))                  newfile();

            else if(e.getActionCommand().equals("open"))        openfile();

            else if(e.getActionCommand().equals("save"))            savefile(textarea.getText());

            else if(e.getActionCommand().equals("save as")){
                Title = "Untitled";
                savefile(textarea.getText());}

            else if(e.getActionCommand().equals("print"))
                try {textarea.print();}
                catch (PrinterException e1) {}

            else if(e.getActionCommand().equals("exit"))            {
                if(edited){
                    int option = JOptionPane.showConfirmDialog(frame, "Do you want to save the changes made to "+Title, "Confirm Exit", JOptionPane.YES_NO_CANCEL_OPTION);
                    if(option ==JOptionPane.YES_OPTION){
                        savefile(textarea.getText());
                        frame.dispose();
                    }
                    if(option == JOptionPane.NO_OPTION)frame.dispose();
                }
                else frame.dispose();
            }

            else if(e.getActionCommand().equals("cut"))         textarea.cut();

            else if(e.getActionCommand().equals("copy"))        textarea.copy();

            else if(e.getActionCommand().equals("paste"))       textarea.paste();

            else if(e.getActionCommand().equals("undo"))    {
                if(undomanager.canUndo())undomanager.undo();
                else     frame.getJMenuBar().getMenu(1).getItem(0).setEnabled(false);
            }

            else if(e.getActionCommand().equals("redo"))    {
                if(undomanager.canRedo())undomanager.redo();
                else frame.getJMenuBar().getMenu(1).getItem(0).setEnabled(false);
            }

            else if(e.getActionCommand().equals("delete"))      {
                if(textarea.getSelectedText()==null)textarea.moveCaretPosition(textarea.getCaretPosition()+1);
                textarea.replaceSelection("");}

            else if(e.getActionCommand().equals("find"))        find();

            else if(e.getActionCommand().equals("go to"))       {
                String i = JOptionPane.showInputDialog(frame, "Enter Line Number", "Go To Line", JOptionPane.PLAIN_MESSAGE);
                if(i!=null)
                    try { textarea.setCaretPosition(textarea.getLineStartOffset(Integer.valueOf(i)-1));} catch (Exception e1) {}
            }

            else if(e.getActionCommand().equals("select all"))          {
                try {
                    textarea.setSelectionStart(0);
                    textarea.setSelectionEnd(textarea.getLineEndOffset(textarea.getLineCount()-1));
                } catch (BadLocationException e1) {}}

            else if(e.getActionCommand().equals("time/date"))       textarea.insert(new SimpleDateFormat("dd/MM/yyyy hh:mm:ss").
                    format(Calendar.getInstance().getTime()),textarea.getCaretPosition());

            else if(e.getActionCommand().equals("wordwrap")){
                boolean s = frame.getJMenuBar().getMenu(2).getItem(1).isSelected();
                textarea.setLineWrap(s);
                frame.getJMenuBar().getMenu(3).getItem(0).setEnabled(!s);
                if(s && frame.getJMenuBar().getMenu(3).getItem(0).isSelected()){
                    frame.getJMenuBar().getMenu(3).getItem(0).setSelected(false);
                    frame.remove(status);
                    frame.setSize(frame.getWidth(), frame.getHeight()-1);
                }
            }
            else if(e.getActionCommand().equals("format")){
                format();
            }
            else if(e.getActionCommand().equals("statusbar")){
                boolean s = frame.getJMenuBar().getMenu(3).getItem(0).isSelected();
                if(s){
                    frame.add(status,BorderLayout.SOUTH);
                    frame.setSize(frame.getWidth(), frame.getHeight()+1);
                }
                else{
                    frame.remove(status);
                    frame.setSize(frame.getWidth(), frame.getHeight()-1);
                }
            }
        }
    }
}
